<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Documentation SimpleTest : &eacute;tendre le testeur
unitaire avec des classes d'attentes suppl&eacute;mentaires</title>
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
</head>
<body>
<div class="menu_back">
<div class="menu"><a href="index.html">SimpleTest</a> | <a
	href="overview.html">Overview</a> | <a
	href="unit_test_documentation.html">Unit tester</a> | <a
	href="group_test_documentation.html">Group tests</a> | <a
	href="mock_objects_documentation.html">Mock objects</a> | <a
	href="partial_mocks_documentation.html">Partial mocks</a> | <a
	href="reporter_documentation.html">Reporting</a> | <a
	href="expectation_documentation.html">Expectations</a> | <a
	href="web_tester_documentation.html">Web tester</a> | <a
	href="form_testing_documentation.html">Testing forms</a> | <a
	href="authentication_documentation.html">Authentication</a> | <a
	href="browser_documentation.html">Scriptable browser</a></div>
</div>
<h1>Documentation sur les attentes</h1>
This page...
<ul>
	<li>Utiliser les attentes <a href="#fantaisie">pour des tests
	plus pr&eacute;cis avec des objets fantaisie</a></li>
	<li><a href="#comportement">Changer le comportement d'un objet
	fantaisie</a> avec des attentes</li>
	<li><a href="#etendre">Cr&eacute;er des attentes</a></li>
	<li>Par dessous SimpleTest <a href="#unitaire">utilise des
	classes d'attente</a></li>
</ul>
<div class="content">
<p><a class="target" name="fantaisie">
<h2>Plus de contr&ocirc;le sur les objets fantaisie</h2>
</a></p>
<p>Le comportement par d&eacute;faut des <a
	href="mock_objects_documentation.html">objets fantaisie</a> dans <a
	href="http://sourceforge.net/projects/simpletest/">SimpleTest</a> est
soit une correspondance identique sur l'argument, soit l'acceptation de
n'importe quel argument. Pour la plupart des tests, c'est suffisant.
Cependant il est parfois n&eacute;cessaire de ramollir un
sc&eacute;nario de test.</p>
<p>Un des endroits o&ugrave; un test peut &ecirc;tre trop
serr&eacute; est la reconnaissance textuelle. Prenons l'exemple d'un
composant qui produirait un message d'erreur utile lorsque quelque chose
plante. Il serait utile de tester que l'erreur correcte est
renvoy&eacute;e, mais le texte proprement dit risque d'&ecirc;tre
plut&ocirc;t long. Si vous testez le texte dans son ensemble alors
&agrave; chaque modification de ce m&ecirc;me message -- m&ecirc;me un
point ou une virgule -- vous aurez &agrave; revenir sur la suite de test
pour la modifier.</p>
<p>Voici un cas concret, nous avons un service d'actualit&eacute;s
qui a &eacute;chou&eacute; dans sa tentative de connexion &agrave; sa
source distante.
<pre>
<strong>class NewsService {
    ...
    function publish(&amp;$writer) {
        if (! $this-&gt;isConnected()) {
            $writer-&gt;write('Cannot connect to news service "' .
                    $this-&gt;_name . '" at this time. ' .
                    'Please try again later.');
        }
        ...
    }
}</strong>
</pre> L&agrave; il envoie son contenu vers un classe <span class="new_code">Writer</span>.
Nous pourrions tester ce comportement avec un <span class="new_code">MockWriter</span>...
<pre>
class TestOfNewsService extends UnitTestCase {
    ...
    function testConnectionFailure() {<strong>
        $writer = &amp;new MockWriter($this);
        $writer-&gt;expectOnce('write', array(
                'Cannot connect to news service ' .
                '"BBC News" at this time. ' .
                'Please try again later.'));
        
        $service = &amp;new NewsService('BBC News');
        $service-&gt;publish($writer);
        
        $writer-&gt;tally();</strong>
    }
}
</pre> C'est un bon exemple d'un test fragile. Si nous d&eacute;cidons
d'ajouter des instructions compl&eacute;mentaires, par exemple proposer
une source d'actualit&eacute;s alternative, nous casserons nos tests par
la m&ecirc;me occasion sans pourtant avoir modifi&eacute; une seule
fonctionnalit&eacute;.
</p>
<p>Pour contourner ce probl&egrave;me, nous voudrions utiliser un
test avec une expression rationnelle plut&ocirc;t qu'une correspondance
exacte. Nous pouvons y parvenir avec...
<pre>
class TestOfNewsService extends UnitTestCase {
    ...
    function testConnectionFailure() {
        $writer = &amp;new MockWriter($this);<strong>
        $writer-&gt;expectOnce(
                'write',
                array(new WantedPatternExpectation('/cannot connect/i')));</strong>
        
        $service = &amp;new NewsService('BBC News');
        $service-&gt;publish($writer);
        
        $writer-&gt;tally();
    }
}
</pre> Plut&ocirc;t que de transmettre le param&egrave;tre attendu au <span
	class="new_code">MockWriter</span>, nous envoyons une classe d'attente
appel&eacute;e <span class="new_code">WantedPatternExpectation</span>.
L'objet fantaisie est suffisamment &eacute;l&eacute;gant pour
reconna&icirc;tre qu'il s'agit d'un truc sp&eacute;cial et pour le
traiter diff&eacute;remment. Plut&ocirc;t que de comparer l'argument
entrant &agrave; cet objet, il utilise l'objet attente lui-m&ecirc;me
pour ex&eacute;cuter le test.
</p>
<p><span class="new_code">WantedPatternExpectation</span> utilise
l'expression rationnelle pour la comparaison avec son constructeur. A
chaque fois qu'une comparaison est fait &agrave; travers <span
	class="new_code">MockWriter</span> par rapport &agrave; cette classe
attente, elle fera un <span class="new_code">preg_match()</span> avec ce
motif. Dans notre sc&eacute;nario de test ci-dessus, aussi longtemps que
la cha&icirc;ne "cannot connect" appara&icirc;t dans le texte, la
fantaisie transmettra un succ&egrave;s au testeur unitaire. Peu importe
le reste du texte.</p>
<p>Les classes attente possibles sont...
<table>
	<tbody>
		<tr>
			<td><span class="new_code">EqualExpectation</span></td>
			<td>Une &eacute;galit&eacute;, plut&ocirc;t que la plus forte
			comparaison &agrave; l'identique</td>
		</tr>
		<tr>
			<td><span class="new_code">NotEqualExpectation</span></td>
			<td>Une comparaison sur la non-&eacute;galit&eacute;</td>
		</tr>
		<tr>
			<td><span class="new_code">IndenticalExpectation</span></td>
			<td>La v&eacute;rification par d&eacute;faut de l'objet
			fantaisie qui doit correspondre exactement</td>
		</tr>
		<tr>
			<td><span class="new_code">NotIndenticalExpectation</span></td>
			<td>Inverse la logique de l'objet fantaisie</td>
		</tr>
		<tr>
			<td><span class="new_code">WantedPatternExpectation</span></td>
			<td>Utilise une expression rationnelle Perl pour comparer une
			cha&icirc;ne</td>
		</tr>
		<tr>
			<td><span class="new_code">NoUnwantedPatternExpectation</span></td>
			<td>Passe seulement si l'expression rationnelle Perl
			&eacute;choue</td>
		</tr>
		<tr>
			<td><span class="new_code">IsAExpectation</span></td>
			<td>V&eacute;rifie le type ou le nom de la classe uniquement</td>
		</tr>
		<tr>
			<td><span class="new_code">NotAExpectation</span></td>
			<td>L'oppos&eacute; de <span class="new_code">IsAExpectation</span></td>
		</tr>
		<tr>
			<td><span class="new_code">MethodExistsExpectation</span></td>
			<td>V&eacute;rifie si la m&eacute;thode est disponible sur un
			objet</td>
		</tr>
	</tbody>
</table>
La plupart utilisent la valeur attendue dans le constructeur. Les
exceptions sont les v&eacute;rifications sur motif, qui utilisent une
expression rationnelle, ainsi que <span class="new_code">IsAExpectation</span>
et <span class="new_code">NotAExpectation</span>, qui prennent un type
ou un nom de classe comme cha&icirc;ne.
</p>

<p><a class="target" name="comportement">
<h2>Utiliser les attentes pour contr&ocirc;ler les bouchons serveur</h2>
</a></p>
<p>Les classes attente peuvent servir &agrave; autre chose que
l'envoi d'assertions depuis les objets fantaisie, afin de choisir le
comportement d'un <a href="mock_objects_documentation.html">objet
fantaisie</a> ou celui d'un <a href="server_stubs_documentation.html">bouchon
serveur</a>. A chaque fois qu'une liste d'arguments est donn&eacute;e, une
liste d'objets d'attente peut &ecirc;tre ins&eacute;r&eacute;e &agrave;
la place.</p>
<p>Mettons que nous voulons qu'un bouchon serveur d'autorisation
simule une connexion r&eacute;ussie seulement si il re&ccedil;oit un
objet de session valide. Nous pouvons y arriver avec ce qui suit...
<pre>
Stub::generate('Authorisation');
<strong>
$authorisation = new StubAuthorisation();
$authorisation-&gt;setReturnValue(
        'isAllowed',
        true,
        array(new IsAExpectation('Session', 'Must be a session')));
$authorisation-&gt;setReturnValue('isAllowed', false);</strong>
</pre> Le comportement par d&eacute;faut du bouchon serveur est d&eacute;fini
pour renvoyer <span class="new_code">false</span> quand <span
	class="new_code">isAllowed</span> est appel&eacute;. Lorsque nous
appelons cette m&eacute;thode avec un unique param&egrave;tre qui est un
objet <span class="new_code">Session</span>, il renverra <span
	class="new_code">true</span>. Nous avons aussi ajout&eacute; un
deuxi&egrave;me param&egrave;tre comme message. Il sera affich&eacute;
dans le message d'erreur de l'objet fantaisie si l'attente est la cause
de l'&eacute;chec.
</p>
<p>Ce niveau de sophistication est rarement utile : il n'est inclut
que pour &ecirc;tre complet.</p>

<p><a class="target" name="etendre">
<h2>Cr&eacute;er vos propres attentes</h2>
</a></p>
<p>Les classes d'attentes ont une structure tr&egrave;s simple.
Tellement simple qu'il devient tr&egrave;s simple de cr&eacute;er vos
propres version de logique pour des tests utilis&eacute;s couramment.</p>
<p>Par exemple voici la cr&eacute;ation d'une classe pour tester la
validit&eacute; d'adresses IP. Pour fonctionner correctement avec les
bouchons serveurs et les objets fantaisie, cette nouvelle classe
d'attente devrait &eacute;tendre <span class="new_code">SimpleExpectation</span>...

<pre>
<strong>class ValidIp extends SimpleExpectation {
    
    function test($ip) {
        return (ip2long($ip) != -1);
    }
    
    function testMessage($ip) {
        return "Address [$ip] should be a valid IP address";
    }
}</strong>
</pre> Il n'y a v&eacute;ritablement que deux m&eacute;thodes &agrave; mettre
en place. La m&eacute;thode <span class="new_code">test()</span> devrait
renvoyer un <span class="new_code">true</span> si l'attente doit passer,
et une erreur <span class="new_code">false</span> dans le cas contraire.
La m&eacute;thode <span class="new_code">testMessage()</span> ne devrait
renvoyer que du texte utile &agrave; la compr&eacute;hension du test en
lui-m&ecirc;me.
</p>
<p>Cette classe peut d&eacute;sormais &ecirc;tre employ&eacute;e
&agrave; la place des classes d'attente pr&eacute;c&eacute;dentes.</p>

<p><a class="target" name="unitaire">
<h2>Sous le capot du testeur unitaire</h2>
</a></p>
<p>Le <a href="http://sourceforge.net/projects/simpletest/">framework
de test unitaire SimpleTest</a> utilise aussi dans son coeur des classes
d'attente pour la <a href="unit_test_documentation.html">classe
UnitTestCase</a>. Nous pouvons aussi tirer parti de ces m&eacute;canismes
pour r&eacute;utiliser nos propres classes attente &agrave;
l'int&eacute;rieur m&ecirc;me des suites de test.</p>
<p>La m&eacute;thode la plus directe est d'utiliser la
m&eacute;thode <span class="new_code">SimpleTest::assertExpectation()</span>
pour effectuer le test...
<pre>
<strong>class TestOfNetworking extends UnitTestCase {
    ...
    function testGetValidIp() {
        $server = &amp;new Server();
        $this-&gt;assertExpectation(
                new ValidIp(),
                $server-&gt;getIp(),
                'Server IP address-&gt;%s');
    }
}</strong>
</pre> C'est plut&ocirc;t sale par rapport &agrave; notre syntaxe habituelle
du type <span class="new_code">assert...()</span>.
</p>
<p>Pour un cas aussi simple, nous cr&eacute;ons d'ordinaire une
m&eacute;thode d'assertion distincte en utilisant la classe d'attente.
Supposons un instant que notre attente soit un peu plus
compliqu&eacute;e et que par cons&eacute;quent nous souhaitions la
r&eacute;utiliser, nous obtenons...
<pre>
class TestOfNetworking extends UnitTestCase {
    ...<strong>
    function assertValidIp($ip, $message = '%s') {
        $this-&gt;assertExpectation(new ValidIp(), $ip, $message);
    }</strong>
    
    function testGetValidIp() {
        $server = &amp;new Server();<strong>
        $this-&gt;assertValidIp(
                $server-&gt;getIp(),
                'Server IP address-&gt;%s');</strong>
    }
}
</pre> Il est peu probable que nous ayons besoin de ce niveau de
contr&ocirc;le sur la machinerie de test. Il est assez rare que le
besoin d'une attente d&eacute;passe le stade de la reconnaissance d'un
motif. De plus, les classes d'attente complexes peuvent rendre les tests
difficiles &agrave; lire et &agrave; d&eacute;boguer. Ces
m&eacute;canismes sont v&eacute;ritablement l&agrave; pour les auteurs
de syst&egrave;me qui &eacute;tendront le framework de test pour leurs
propres outils de test.
</p>

</div>
References and related information...
<ul>
	<li>La page du projet SimpleTest sur <a
		href="http://sourceforge.net/projects/simpletest/">SourceForge</a>.</li>
	<li>La page de t&eacute;l&eacute;chargement de SimpleTest sur <a
		href="http://www.lastcraft.com/simple_test.php">LastCraft</a>.</li>
	<li>Les attentes imitent les contraintes dans <a
		href="http://www.jmock.org/">JMock</a>.</li>
	<li><a href="http://simpletest.sourceforge.net/">L'API
	compl&egrave;te pour SimpleTest</a> r&eacute;alis&eacute; avec PHPDoc.</li>
</ul>
<div class="menu_back">
<div class="menu"><a href="index.html">SimpleTest</a> | <a
	href="overview.html">Overview</a> | <a
	href="unit_test_documentation.html">Unit tester</a> | <a
	href="group_test_documentation.html">Group tests</a> | <a
	href="mock_objects_documentation.html">Mock objects</a> | <a
	href="partial_mocks_documentation.html">Partial mocks</a> | <a
	href="reporter_documentation.html">Reporting</a> | <a
	href="expectation_documentation.html">Expectations</a> | <a
	href="web_tester_documentation.html">Web tester</a> | <a
	href="form_testing_documentation.html">Testing forms</a> | <a
	href="authentication_documentation.html">Authentication</a> | <a
	href="browser_documentation.html">Scriptable browser</a></div>
</div>
<div class="copyright">Copyright<br>
Marcus Baker 2006</div>
</body>
</html>
